/*!
\copyright  Copyright (c) 2023 - 2025 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      Load files in DSP

This API will load files in DSP and remember them and their DSP handle for future use.

*/

#include "downloadable_file.h"
#include <list.h>
#include <logging.h>
#include <operators.h>

typedef struct
{
    FILE_INDEX file_index;
    DataFileID dsp_handle;
    unsigned num_of_users;
} loaded_file_t;

typedef struct
{
    Operator operator;
    FILE_INDEX file_index;
} operator_file_user_t;

static const list_config_t loaded_files_list_config =
{
    .type = list_type_linked_single,
    .element_store.size = sizeof(loaded_file_t),
};

static const list_config_t operator_file_users_list_config =
{
    .type = list_type_linked_single,
    .element_store.size = sizeof(operator_file_user_t),
};

static list_t loaded_files_list;
static list_t operator_file_users_list;

static FILE_INDEX downloadable_GetFileIndex(const char *filename)
{
    FILE_INDEX file_index = FileFind(FILE_ROOT, filename, strlen(filename));
    if(file_index == FILE_NONE)
    {
        DEBUG_LOG_PANIC("downloadable_GetFileIndex: Failed to find file %s", filename);
    }
    return file_index;
}

static DataFileID downloadable_LoadFileOnDsp(FILE_INDEX file_index, OperatorDataFileType type, OperatorDataStorageType storage)
{
    DataFileID dsp_handle = OperatorDataLoadEx(file_index, type, storage, FALSE);
    if (dsp_handle == DATA_FILE_ID_INVALID)
    {
        DEBUG_LOG_PANIC("downloadable_LoadFileOnDsp: Failed to load file %u", file_index);
    }
    return dsp_handle;
}

static list_t * downloadable_GetLoadedFiles(void)
{
    if (loaded_files_list == NULL)
    {
        loaded_files_list = ListCreate(&loaded_files_list_config);
    }
    return &loaded_files_list;
}

static list_t * downloadable_GetOperatorFileUsers(void)
{
    if (operator_file_users_list == NULL)
    {
        operator_file_users_list = ListCreate(&operator_file_users_list_config);
    }
    return &operator_file_users_list;
}

static loaded_file_t * downloadable_GetLoadedFile(FILE_INDEX file_index)
{
    list_t *loaded_files = downloadable_GetLoadedFiles();

    LIST_FOREACH(loaded_files)
    {
        loaded_file_t *entry = ListGetCurrentElementAddress(loaded_files);
        if (entry->file_index == file_index)
        {
            return entry;
        }
    }

    return NULL;
}

static loaded_file_t * downloadable_CreateLoadedFileEntry(FILE_INDEX file_index, DataFileID dsp_handle)
{
    list_t *loaded_files = downloadable_GetLoadedFiles();
    loaded_file_t file =
    {
        .file_index = file_index,
        .dsp_handle = dsp_handle,
    };
    ListAppend(loaded_files, &file);
    ListGotoTail(loaded_files);
    return ListGetCurrentElementAddress(loaded_files);
}

static void downloadable_CreateOperatorFileUserEntry(Operator operator, FILE_INDEX file_index)
{
    list_t *users = downloadable_GetOperatorFileUsers();
    operator_file_user_t user =
    {
        .operator = operator,
        .file_index = file_index,
    };
    ListAppend(users, &user);
}

static loaded_file_t * downloadable_AddFileUser(const char *filename, OperatorDataFileType type, OperatorDataStorageType storage)
{
    FILE_INDEX file_index = downloadable_GetFileIndex(filename);
    loaded_file_t *loaded_file = downloadable_GetLoadedFile(file_index);
    if (loaded_file == NULL)
    {
        loaded_file = downloadable_CreateLoadedFileEntry(file_index, downloadable_LoadFileOnDsp(file_index, type, storage));
    }
    loaded_file->num_of_users++;
    return loaded_file;
}

static void downloadable_RemoveFileUser(FILE_INDEX file_index)
{
    loaded_file_t *loaded_file = PanicNull(downloadable_GetLoadedFile(file_index));
    loaded_file->num_of_users--;
    if (loaded_file->num_of_users == 0)
    {
        PanicFalse(OperatorDataUnloadEx(loaded_file->dsp_handle));
        ListRemoveCurrentElement(downloadable_GetLoadedFiles());
    }
}

DataFileID Downloadable_AddFileUser(const char *filename, OperatorDataFileType type, OperatorDataStorageType storage)
{
    loaded_file_t *loaded_file = downloadable_AddFileUser(filename, type, storage);
    return loaded_file->dsp_handle;
}

void Downloadable_RemoveFileUser(const char *filename)
{
    FILE_INDEX file_index = downloadable_GetFileIndex(filename);
    downloadable_RemoveFileUser(file_index);
}

DataFileID Downloadable_AddOperatorFileUser(Operator operator, const char *filename, OperatorDataFileType type, OperatorDataStorageType storage)
{
    PanicFalse(operator != INVALID_OPERATOR);
    loaded_file_t *loaded_file = downloadable_AddFileUser(filename, type, storage);
    downloadable_CreateOperatorFileUserEntry(operator, loaded_file->file_index);
    return loaded_file->dsp_handle;
}

void Downloadable_RemoveOperatorFileUser(Operator operator)
{
    list_t *users = downloadable_GetOperatorFileUsers();
    operator_file_user_t user;
    
    ListGotoHead(users);
    while(!ListIsAtEndOfList(users))
    {
        ListGetCurrentElement(users, &user);

        if (user.operator == operator)
        {
            downloadable_RemoveFileUser(user.file_index);
            ListRemoveCurrentElement(users);
        }
        else
        {
            ListGotoNext(users);
        }
    }
}

#ifdef HOSTED_TEST_ENVIRONMENT
void Downloadable_FileTestReset(void)
{
    if (loaded_files_list)
    {
        ListDestroy(&loaded_files_list);
    }
    if (operator_file_users_list)
    {
        ListDestroy(&operator_file_users_list);
    }
}
#endif